Public Class JoinDesigner


    Protected _Owner As Core.Functions.MetisResultFunction
    Protected WithEvents _JoinList As Core.Functions.MetisJoinList
    Protected WithEvents _FuncList As Core.Functions.MetisRefFunctionList
    'Protected _TableList As DAMfunctionList
    Protected _SelectJoinOnClick As Boolean = True
    Friend _SelectedJoin As Core.Functions.MetisJoin
    Friend _SelectedTable As Core.Functions.MetisRefFunction
    Protected _TableContextMenuStrip As ContextMenuStrip
    Protected _JoinContextMenuStrip As ContextMenuStrip

    Private _JoinLineOfs As Integer = 12
    Private _ClickPos As Drawing.Point
    Private _ClickButton As MouseButtons
    Private _ClickCount As Integer



    Public Event JoinClick(ByVal Sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
    Public Event JoinDoubleClick(ByVal Sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)


    Sub Init(ByVal Owner As Core.Functions.MetisResultFunction)
        _Owner = Owner
        _JoinList = _Owner.Joins
        _FuncList = _Owner.FunctionReferences

        For i As Integer = 0 To _FuncList.Count - 1
            Dim tmpListChangedEventArgs As New System.ComponentModel.ListChangedEventArgs(System.ComponentModel.ListChangedType.ItemAdded, i)
            HandleFuncListChanged(_FuncList, tmpListChangedEventArgs)
        Next

        Me.VerticalScroll.Visible = True
        Me.HorizontalScroll.Visible = True
    End Sub

    Sub Init(ByVal Owner As Core.Functions.MetisResultFunction, ByVal DropDownMenu As ContextMenuStrip)
        _TableContextMenuStrip = DropDownMenu
        Init(Owner)
    End Sub

    Private Sub HandleJoinListChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ListChangedEventArgs) Handles _JoinList.ListChanged
        _SelectedJoin = Nothing
        Refresh()
    End Sub

    Private Sub HandleTableMoved(ByVal sender As Object, ByVal e As System.EventArgs)
        _SelectedJoin = Nothing
        Refresh()
    End Sub

    Private Sub HandleFuncListChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ListChangedEventArgs) Handles _FuncList.ListChanged
        Select Case e.ListChangedType
            Case System.ComponentModel.ListChangedType.ItemAdded
                Dim tmpRefFunc As Core.Functions.MetisRefFunction
                tmpRefFunc = DirectCast(sender, Core.Functions.MetisRefFunctionList).Item(e.NewIndex)
                Dim jdt As New JoinDesignerTable(tmpRefFunc)
                jdt.TopLevel = False
                jdt.Location = tmpRefFunc.Position
                jdt.Parent = Me
                jdt.ContextMenuStrip = _TableContextMenuStrip
                Controls.Add(jdt)
                jdt.Show()
                AddHandler jdt.LocationChanged, AddressOf HandleTableMoved
                AddHandler jdt.Resize, AddressOf HandleTableMoved

            Case System.ComponentModel.ListChangedType.ItemDeleted
                'Dim tmpRefFunc As Core.Functions.MetisRefFunction
                'tmpRefFunc = DirectCast(sender, Core.Functions.MetisRefFunctionList).Item(e.NewIndex)
                For Each item As Control In Controls
                    If TypeOf item Is JoinDesignerTable AndAlso _FuncList.IndexOf(DirectCast(item, JoinDesignerTable).Represents) < 0 Then
                        RemoveHandler item.LocationChanged, AddressOf HandleTableMoved
                        RemoveHandler item.Resize, AddressOf HandleTableMoved
                        Controls.Remove(item)
                        Exit For
                    End If
                Next

            Case System.ComponentModel.ListChangedType.ItemChanged
                If e.PropertyDescriptor.Name = "Alias" Then
                    Dim tmpJDT As JoinDesignerTable = ControlByFuncRef(_FuncList(e.NewIndex))
                    If tmpJDT IsNot Nothing Then tmpJDT.Text = _FuncList(e.NewIndex).Alias
                End If

        End Select
        Refresh()
    End Sub

    Private Function ControlByFuncRef(ByVal RefFunc As Core.Functions.MetisRefFunction) As JoinDesignerTable
        For Each item As Control In Controls
            If (TypeOf item Is JoinDesignerTable AndAlso DirectCast(item, JoinDesignerTable).Represents.Equals(RefFunc)) Then
                Return item
            End If
        Next
        Return Nothing
    End Function

    Private Enum JoinEndType
        RightArrow = 1
        LeftArrow = -1
        RightRect = 2
        LeftRect = -2
    End Enum

    Private Function DrawJoinEnd(ByVal Location As Drawing.Point, ByVal g As Drawing.Graphics, ByVal p As Pen, ByVal JoinEnd As JoinEndType) As Drawing.Point

        If JoinEnd = JoinEndType.LeftArrow Or JoinEnd = JoinEndType.LeftRect Then
            g.DrawLine(p, Location.X + 2, Location.Y + 7, Location.X + _JoinLineOfs, Location.Y + 7)
            If JoinEnd = JoinEndType.LeftArrow Then
                Dim LeftArrowTempl(4) As Drawing.Point
                LeftArrowTempl(0) = New Drawing.Point(Location.X, Location.Y + 7)
                LeftArrowTempl(1) = New Drawing.Point(Location.X + 5, Location.Y + 2)
                LeftArrowTempl(2) = New Drawing.Point(Location.X + 5, Location.Y + 12)
                LeftArrowTempl(3) = New Drawing.Point(Location.X, Location.Y + 7)
                g.FillPolygon(Brushes.Black, LeftArrowTempl)
            Else
                g.FillRectangle(Brushes.Black, New Drawing.Rectangle(Location.X, Location.Y + 5, 2, 5))
            End If
            Return New Drawing.Point(Location.X + _JoinLineOfs, Location.Y + 7)
        Else
            g.DrawLine(p, Location.X - 2, Location.Y + 7, Location.X - _JoinLineOfs, Location.Y + 7)
            If JoinEnd = JoinEndType.RightArrow Then
                Dim RightArrowTempl(4) As Drawing.Point
                RightArrowTempl(0) = New Drawing.Point(Location.X, Location.Y + 7)
                RightArrowTempl(1) = New Drawing.Point(Location.X - 5, Location.Y + 2)
                RightArrowTempl(2) = New Drawing.Point(Location.X - 5, Location.Y + 12)
                RightArrowTempl(3) = New Drawing.Point(Location.X, Location.Y + 7)
                g.FillPolygon(Brushes.Black, RightArrowTempl)
            Else
                g.FillRectangle(Brushes.Black, New Drawing.Rectangle(Location.X - 2, Location.Y + 5, 2, 5))
            End If
            Return New Drawing.Point(Location.X - _JoinLineOfs, Location.Y + 7)
        End If

    End Function


    Private Sub TableJoinDesigner_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint

        If _Owner Is Nothing OrElse _Owner.Joins Is Nothing Then Exit Sub
        Me.SuspendLayout()

        Dim g As System.Drawing.Graphics = e.Graphics

        Dim p As New Pen(Color.Black)

        Dim tmpLeftPoint As Drawing.Point
        Dim tmpRightPoint As Drawing.Point
        For Each j As Core.Functions.MetisJoin In _Owner.Joins

            If j.Equals(_SelectedJoin) Then
                p.Width = 2
            Else
                p.Width = 1
            End If

            Dim tmpLeftTable As JoinDesignerTable = ControlByFuncRef(j.LeftFuncRef)
            Dim tmpRightTable As JoinDesignerTable = ControlByFuncRef(j.RightFuncRef)

            If tmpLeftTable.Location.X + tmpLeftTable.Size.Width / 2 < tmpRightTable.Location.X + tmpRightTable.Size.Width / 2 Then
                Select Case j.JoinType
                    Case Core.Functions.MetisJoinType.InnerJoin
                        tmpLeftPoint = DrawJoinEnd(New Drawing.Point(tmpLeftTable.Right, tmpLeftTable.FieldLocation(j.LeftField).Y), g, p, JoinEndType.LeftRect)
                        tmpRightPoint = DrawJoinEnd(New Drawing.Point(tmpRightTable.Left, tmpRightTable.FieldLocation(j.RightField).Y), g, p, JoinEndType.RightRect)
                        g.DrawLine(p, tmpLeftPoint, tmpRightPoint)
                    Case Core.Functions.MetisJoinType.LeftJoin
                        tmpLeftPoint = DrawJoinEnd(New Drawing.Point(tmpLeftTable.Right, tmpLeftTable.FieldLocation(j.LeftField).Y), g, p, JoinEndType.LeftArrow)
                        tmpRightPoint = DrawJoinEnd(New Drawing.Point(tmpRightTable.Left, tmpRightTable.FieldLocation(j.RightField).Y), g, p, JoinEndType.RightRect)
                        g.DrawLine(p, tmpLeftPoint, tmpRightPoint)
                    Case Core.Functions.MetisJoinType.RightJoin
                        tmpLeftPoint = DrawJoinEnd(New Drawing.Point(tmpRightTable.Left, tmpRightTable.FieldLocation(j.RightField).Y), g, p, JoinEndType.RightArrow)
                        tmpRightPoint = DrawJoinEnd(New Drawing.Point(tmpLeftTable.Right, tmpLeftTable.FieldLocation(j.LeftField).Y), g, p, JoinEndType.LeftRect)

                        g.DrawLine(p, tmpLeftPoint, tmpRightPoint)
                        'If p.Width > 1 Then g.DrawLine(p, tmpLeftPoint.X, tmpLeftPoint.Y + 1, tmpRightPoint.X, tmpRightPoint.Y + 1)
                End Select
            Else
                Select Case j.JoinType
                    Case Core.Functions.MetisJoinType.InnerJoin
                        tmpLeftPoint = DrawJoinEnd(New Drawing.Point(tmpLeftTable.Left, tmpLeftTable.FieldLocation(j.LeftField).Y), g, p, JoinEndType.RightRect)
                        tmpRightPoint = DrawJoinEnd(New Drawing.Point(tmpRightTable.Right, tmpRightTable.FieldLocation(j.RightField).Y), g, p, JoinEndType.LeftRect)
                        g.DrawLine(p, tmpLeftPoint, tmpRightPoint)
                    Case Core.Functions.MetisJoinType.LeftJoin
                        tmpLeftPoint = DrawJoinEnd(New Drawing.Point(tmpLeftTable.Left, tmpLeftTable.FieldLocation(j.LeftField).Y), g, p, JoinEndType.RightArrow)
                        tmpRightPoint = DrawJoinEnd(New Drawing.Point(tmpRightTable.Right, tmpRightTable.FieldLocation(j.RightField).Y), g, p, JoinEndType.LeftRect)
                        g.DrawLine(p, tmpLeftPoint, tmpRightPoint)
                    Case Core.Functions.MetisJoinType.RightJoin
                        tmpLeftPoint = DrawJoinEnd(New Drawing.Point(tmpRightTable.Right, tmpRightTable.FieldLocation(j.RightField).Y), g, p, JoinEndType.LeftArrow)
                        tmpRightPoint = DrawJoinEnd(New Drawing.Point(tmpLeftTable.Left, tmpLeftTable.FieldLocation(j.LeftField).Y), g, p, JoinEndType.RightRect)
                        g.DrawLine(p, tmpLeftPoint, tmpRightPoint)
                        'If p.Width > 1 Then g.DrawLine(p, tmpLeftPoint.X, tmpLeftPoint.Y + 1, tmpRightPoint.X, tmpRightPoint.Y + 1)
                End Select
            End If
        Next

        Me.ResumeLayout()

    End Sub


    Public Function JoinByLocation(ByVal p As Drawing.Point) As Core.Functions.MetisJoin
        Dim tmpLeftPoint As Drawing.Point
        Dim tmpRightPoint As Drawing.Point
        Dim tmpRelPos As New Drawing.Point

        Dim tmpLeft As Integer
        Dim tmpTop As Integer
        Dim tmpWidth As Integer
        Dim tmpHeight As Integer

        For Each j As Core.Functions.MetisJoin In _Owner.Joins

            Dim tmpLeftTable As JoinDesignerTable = ControlByFuncRef(j.LeftFuncRef)
            Dim tmpRightTable As JoinDesignerTable = ControlByFuncRef(j.RightFuncRef)

            If tmpLeftTable.Location.X + tmpLeftTable.Size.Width / 2 < tmpRightTable.Location.X + tmpRightTable.Size.Width / 2 Then
                tmpLeftPoint.X = tmpLeftTable.Right + _JoinLineOfs
                tmpLeftPoint.Y = tmpLeftTable.FieldLocation(j.LeftField).Y + 7

                tmpRightPoint.X = tmpRightTable.Left - _JoinLineOfs
                tmpRightPoint.Y = tmpRightTable.FieldLocation(j.RightField).Y + 7

            Else
                tmpRightPoint.X = tmpLeftTable.Left - _JoinLineOfs
                tmpRightPoint.Y = tmpLeftTable.FieldLocation(j.LeftField).Y + 7

                tmpLeftPoint.X = tmpRightTable.Right + _JoinLineOfs
                tmpLeftPoint.Y = tmpRightTable.FieldLocation(j.RightField).Y + 7
            End If



            tmpLeft = Math.Min(tmpLeftPoint.X, tmpRightPoint.X)
            tmpTop = Math.Min(tmpLeftPoint.Y, tmpRightPoint.Y)
            tmpWidth = Math.Abs(tmpLeftPoint.X - tmpRightPoint.X)
            tmpHeight = Math.Abs(tmpLeftPoint.Y - tmpRightPoint.Y)

            If _ClickPos.X >= tmpLeft - 1 And _ClickPos.X <= tmpLeft + tmpWidth + 1 AndAlso _
               _ClickPos.Y >= tmpTop - 1 And _ClickPos.Y <= tmpTop + tmpHeight + 1 Then
                If (tmpLeftPoint.X - tmpRightPoint.X) * (tmpLeftPoint.Y - tmpRightPoint.Y) > 0 Then
                    If (_ClickPos.X - tmpLeft >= Math.Truncate(tmpWidth / tmpHeight * (_ClickPos.Y - tmpTop)) - 1 And _
                        _ClickPos.X - tmpLeft <= Math.Truncate(tmpWidth / tmpHeight * (_ClickPos.Y - tmpTop)) + 1) OrElse _
                       (_ClickPos.Y - tmpTop >= Math.Truncate(tmpHeight / tmpWidth * (_ClickPos.X - tmpLeft)) - 1 And _
                        _ClickPos.Y - tmpTop <= Math.Truncate(tmpHeight / tmpWidth * (_ClickPos.X - tmpLeft)) + 1) Then

                        Return j
                        Exit For
                    End If
                Else
                    If (_ClickPos.X - tmpLeft >= Math.Truncate(tmpWidth / tmpHeight * (tmpTop + tmpHeight - _ClickPos.Y)) - 1 And _
                        _ClickPos.X - tmpLeft <= Math.Truncate(tmpWidth / tmpHeight * (tmpTop + tmpHeight - _ClickPos.Y)) + 1) OrElse _
                       (_ClickPos.Y - tmpTop >= Math.Truncate(tmpHeight / tmpWidth * (tmpLeft + tmpWidth - _ClickPos.X)) - 1 And _
                        _ClickPos.Y - tmpTop <= Math.Truncate(tmpHeight / tmpWidth * (tmpLeft + tmpWidth - _ClickPos.X)) + 1) Then

                        Return j
                        Exit For
                    End If
                End If
            End If
        Next
        Return Nothing
    End Function

    Private Sub JoinDesigner_DoubleClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.DoubleClick
        Dim tmpJoin As Core.Functions.MetisJoin = JoinByLocation(_ClickPos)
        If tmpJoin IsNot Nothing Then RaiseEvent JoinDoubleClick(tmpJoin, New System.Windows.Forms.MouseEventArgs(_ClickButton, _ClickCount, _ClickPos.X, _ClickPos.Y, 0))
    End Sub

    Private Sub JoinDesigner_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Click
        Dim tmpJoin As Core.Functions.MetisJoin = JoinByLocation(_ClickPos)

        If _SelectJoinOnClick Then
            _SelectedJoin = tmpJoin
            Me.Refresh()
        End If

        If tmpJoin IsNot Nothing Then
            If _JoinContextMenuStrip IsNot Nothing AndAlso _ClickButton = Windows.Forms.MouseButtons.Right Then _JoinContextMenuStrip.Show(Me.PointToScreen(_ClickPos))
            RaiseEvent JoinClick(tmpJoin, New System.Windows.Forms.MouseEventArgs(_ClickButton, _ClickCount, _ClickPos.X, _ClickPos.Y, 0))
        End If

    End Sub

    Private Sub JoinDesigner_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown
        _ClickPos = New System.Drawing.Point(e.X, e.Y)
        _ClickButton = e.Button
        _ClickCount = e.Clicks
    End Sub

    Public Property SelectJoinOnClick() As Boolean
        Get
            Return _SelectJoinOnClick
        End Get
        Set(ByVal value As Boolean)
            _SelectJoinOnClick = value
            If Not _SelectJoinOnClick Then
                _SelectedJoin = Nothing
                Refresh()
            End If
        End Set
    End Property

    Public ReadOnly Property SelectedJoin() As Core.Functions.MetisJoin
        Get
            Return _SelectedJoin
        End Get
    End Property

    Public ReadOnly Property SelectedTable() As Core.Functions.MetisRefFunction
        Get
            Return _SelectedTable
        End Get
    End Property

    Public Property TableContextMenuStrip() As ContextMenuStrip
        Get
            Return _TableContextMenuStrip
        End Get
        Set(ByVal value As ContextMenuStrip)
            _TableContextMenuStrip = value
            For Each ctrl As Control In Controls
                If TypeOf ctrl Is JoinDesignerTable Then ctrl.ContextMenuStrip = _TableContextMenuStrip
            Next
        End Set
    End Property

    Public Property JoinContextMenuStrip() As ContextMenuStrip
        Get
            Return _JoinContextMenuStrip
        End Get
        Set(ByVal value As ContextMenuStrip)
            _JoinContextMenuStrip = value
        End Set
    End Property

End Class
